/*
 * Copyright (C) 2005 Christopher J. Stawarz <cstawarz@cfa.harvard.edu>
 *
 * This file is part of PySL.
 *
 * PySL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PySL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PySL; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <Python.h>

/* For Python 2.2 compatibility */
#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif
#if !defined(PyDoc_STRVAR)
#define PyDoc_STRVAR(name,str) static char name[] = str
#endif

#include <libnumarray.h>
#include <slang.h>

/* Useful stuff from _slang.h */
extern int _SLstack_depth(void);
extern char* _SLstringize_object(SLang_Any_Type *);
extern SLang_NameSpace_Type* _SLns_find_namespace(char *);
extern int _SLarray_next_index(int *, int *, unsigned int);

/* Pointers to type and error objects */
static PyObject *NAObjectArray_Type;
static PyObject *PySLSLangStruct_Type;
static PyObject *PySLSLangError;
static PyObject *PySLNameError;

/* Pointers to necessary S-Lang intrinsic functions */
static SLang_Name_Type *pysl_assoc_get_keys;
static SLang_Name_Type *pysl_assoc_get_values;
static SLang_Name_Type *pysl_is_struct_type;
static SLang_Name_Type *pysl_get_struct_field_names;
static SLang_Name_Type *pysl_get_struct_field;
static SLang_Name_Type *pysl_set_struct_field;


/*******************************************************************************
 *
 * Generic utilities
 *
 ******************************************************************************/


/*
 * Concatenate n char*'s and return the result.  The resulting string is
 * malloced and must be freed by the caller.
 */
static char *
pysl_strcatn(int n, ...)
{
    va_list argptr;
    char *str = NULL;
    char *buf = NULL;
    size_t buflen = 1;  /* Space for terminating '\0' */
    int i;

    /* Calculate length of result */
    va_start(argptr, n);
    for (i = 0; i < n; i++) {
        str = va_arg(argptr, char*);
	buflen += strlen(str);
    }
    va_end(argptr);

    /* Allocate and initialize buffer */
    if (NULL == (buf = malloc(buflen))) {
        PyErr_NoMemory();
	return NULL;
    }
    strcpy(buf, "");

    /* Build result */
    va_start(argptr, n);
    for (i = 0; i < n; i++) {
        str = va_arg(argptr, char*);
	strcat(buf, str);
    }
    va_end(argptr);

    return buf;
}

/*
 * Return name qualified by S-Lang namespace ns_name, e.g. "ns_name->name" if
 * ns_name is non-NULL, or simply "name" if ns_name is NULL.  The result is
 * malloced and must be freed by the caller.
 */
static char *
pysl_ns_qualify_name(char *name, char *ns_name)
{
    if (NULL == ns_name)
        return pysl_strcatn(1, name);
    return pysl_strcatn(3, ns_name, "->", name);
}


/*******************************************************************************
 *
 * Error handling routines
 *
 ******************************************************************************/


/*
 * Generate a SLangError exception from an error string provided by the
 * S-Lang interpreter.  This function is assigned to SLang_Error_Hook.
 *
 * If a SLangError is already set, then append the new error message to the
 * existing one rather than generating a new exception.  This is necessary
 * because a single S-Lang error condition often generates multiple calls to
 * the error handler, with each one providing a useful message.
 */
static void
pysl_slang_error_handler(char *err)
{
    PyObject *type;
    PyObject *value;
    PyObject *traceback;
    PyObject *new_value;

    PyErr_Fetch(&type, &value, &traceback);

    if (NULL == type) {
        /* No exception set, so create a new one */
        PyErr_Format(PySLSLangError, "\n  %s", err);
	return;
    }

    /* Only mess with an existing exception if it's a SLangError with
     * a string value */
    if (PyErr_GivenExceptionMatches(type, PySLSLangError) &&
        PyString_Check(value))
    {
        new_value = PyString_FromFormat("%s\n  %s", PyString_AsString(value),
	  err);

	if (NULL != new_value) {
	    Py_DECREF(value);
	    value = new_value;
	}
    }

    PyErr_Restore(type, value, traceback);
}

/*
 * Reset the S-Lang interpreter after an error.
 *
 * If a S-Lang error occurred but no exception has been set, set one with a
 * generic error message.  This can happen if an imported S-Lang module sets
 * SLang_Error_Hook, thereby disabling our error handler.
 */
static void
pysl_slang_restart(void)
{
    if ((NULL == PyErr_Occurred()) && SLang_Error) {
        PyErr_Format(PySLSLangError, "S-Lang error occurred [SLang_Error = %i]",
	  SLang_Error);
    }

    SLang_restart(1);
    SLang_Error = 0;
}


/*******************************************************************************
 *
 * SLangName type
 *
 * This type is basically a customized PyCObject that encapsulates a
 * SLang_Name_Type pointer, which points to a S-Lang function known to the
 * S-Lang interpreter.  It's returned by get_function() and passed to
 * execute_function().
 *
 ******************************************************************************/


typedef struct {
    PyObject_HEAD
    SLang_Name_Type *nt;
} PySLSLangName_Object;

static void
pysl_SLangName_dealloc(PySLSLangName_Object *self)
{
    PyObject_DEL(self);
}

static PyObject *
pysl_SLangName_str(PySLSLangName_Object *self)
{
    return PyString_FromString(self->nt->name);
}

static PyTypeObject PySLSLangName_Type = {
    PyObject_HEAD_INIT(NULL)
    0,						/*ob_size*/
    "pysl.SLangName",				/*tp_name*/
    sizeof(PySLSLangName_Object),		/*tp_basicsize*/
    0,						/*tp_itemsize*/
    (destructor)pysl_SLangName_dealloc,		/*tp_dealloc*/
    0,						/*tp_print*/
    0,						/*tp_getattr*/
    0,						/*tp_setattr*/
    0,						/*tp_compare*/
    0,						/*tp_repr*/
    0,						/*tp_as_number*/
    0,						/*tp_as_sequence*/
    0,						/*tp_as_mapping*/
    0,						/*tp_hash */
    0,						/*tp_call*/
    (reprfunc)pysl_SLangName_str,		/*tp_str*/
    0,						/*tp_getattro*/
    0,						/*tp_setattro*/
    0,						/*tp_as_buffer*/
    0,						/*tp_flags*/
    "Opaque pointer to a S-Lang function",	/*tp_doc*/
};


/*******************************************************************************
 *
 * SLangObject type
 *
 * This type is basically a customized PyCObject that encapsulates a
 * SLang_Any_Type pointer.  It provides an opaque container for S-Lang types
 * that don't have a native representation in Python but can be passed from
 * one S-Lang function to another.
 *
 ******************************************************************************/


typedef struct {
    PyObject_HEAD
    SLang_Any_Type *obj;
} PySLSLangObject_Object;

static void
pysl_SLangObject_dealloc(PySLSLangObject_Object *self)
{
    /* I'm almost certain this is okay; it looks like pushing or popping an
     * anytype always results in a new object being created */
    SLang_free_anytype(self->obj);
    PyObject_DEL(self);
}

static PyObject *
pysl_SLangObject_str(PySLSLangObject_Object *self)
{
    PyObject *str_obj;
    char *str;

    /* Get the S-Lang string representation of the object */
    str = _SLstringize_object(self->obj);
    str_obj = PyString_FromString(str);
    SLang_free_slstring(str);

    return str_obj;
}

static PyTypeObject PySLSLangObject_Type = {
    PyObject_HEAD_INIT(NULL)
    0,						/*ob_size*/
    "pysl.SLangObject",				/*tp_name*/
    sizeof(PySLSLangObject_Object),		/*tp_basicsize*/
    0,						/*tp_itemsize*/
    (destructor)pysl_SLangObject_dealloc,	/*tp_dealloc*/
    0,						/*tp_print*/
    0,						/*tp_getattr*/
    0,						/*tp_setattr*/
    0,						/*tp_compare*/
    0,						/*tp_repr*/
    0,						/*tp_as_number*/
    0,						/*tp_as_sequence*/
    0,						/*tp_as_mapping*/
    0,						/*tp_hash */
    0,						/*tp_call*/
    (reprfunc)pysl_SLangObject_str,		/*tp_str*/
    0,						/*tp_getattro*/
    0,						/*tp_setattro*/
    0,						/*tp_as_buffer*/
    0,						/*tp_flags*/
    "Opaque pointer to a S-Lang object",	/*tp_doc*/
};


/*******************************************************************************
 *
 * Python to S-Lang data conversion routines
 *
 ******************************************************************************/


/* This gets called recursively by the push-related functions */
static int pysl_push_pyobject(PyObject *obj);

/*
 * Create a S-Lang array from a NumArray.
 */
static SLang_Array_Type *
pysl_slarray_from_numarray(PyArrayObject *array)
{
    NumarrayType type;
    SLtype sl_type;
    void *data = NULL;
    int dims[MAXDIM];
    unsigned int num_dims;
    unsigned long nbytes;
    int i;

    /* These are used only for casting Complex32 arrays to Complex64 */
    Float32 *indata = NULL;
    Float64 *outdata = NULL;
    unsigned long byteno;

    if (NULL == array) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create S-Lang array from NULL pointer");
	return NULL;
    }

    type = array->descr->type_num;

    switch (type) {
	case tBool: /* Fall through */
	case tInt8:
            sl_type = SLANG_CHAR_TYPE;
	    break;
	case tUInt8:
            sl_type = SLANG_UCHAR_TYPE;
	    break;
	case tInt16:
            sl_type = SLANG_SHORT_TYPE;
	    break;
	case tUInt16:
            sl_type = SLANG_USHORT_TYPE;
	    break;
	case tInt32:
            sl_type = SLANG_INT_TYPE;
	    break;
	case tUInt32:
            sl_type = SLANG_UINT_TYPE;
	    break;
#if LP64
	case tInt64:
            sl_type = SLANG_LONG_TYPE;
	    break;
	case tUInt64:
            sl_type = SLANG_ULONG_TYPE;
	    break;
#endif
	case tFloat32:
            sl_type = SLANG_FLOAT_TYPE;
	    break;
	case tFloat64:
            sl_type = SLANG_DOUBLE_TYPE;
	    break;
	case tComplex32: /* Fall through (needs special-case handling below) */
	case tComplex64:
            sl_type = SLANG_COMPLEX_TYPE;
	    break;
	default:
            PyErr_Format(PyExc_TypeError,
	      "cannot convert %s NumArray to S-Lang array",
	      NA_typeNoToName(type));
	    return NULL;
    }

    /* Cast number of dimensions from int to unsigned int and each dimension
     * from maybelong to int */
    num_dims = (unsigned int)(array->nd);
    for (i = 0; i < num_dims; i++)
        dims[i] = (int)(array->dimensions[i]);

    /* Allocate memory for the S-Lang array */
    nbytes = NA_NBYTES(array);
    if (tComplex32 == type)  nbytes *= sizeof(Complex64) / sizeof(Complex32);
    if (NULL == (data = malloc(nbytes))) {
        PyErr_NoMemory();
	return NULL;
    }

    /* Copy the array data */
    if (tComplex32 != type) {
        memcpy(data, NA_OFFSETDATA(array), nbytes);
    } else {
        /* Must cast Complex32 array to Complex64, as S-Lang doesn't have
	 * a complex type with 32-bit components */
        indata = (Float32*)NA_OFFSETDATA(array);
        outdata = (Float64*)data;
	for (byteno = 0; byteno < nbytes; byteno += sizeof(Float64))
	    *(outdata++) = (Float64)(*(indata++));
    }

    return SLang_create_array(sl_type, 0, data, dims, num_dims);
}

/*
 * Build a num_dims-element tuple containing the elements of the array dims.
 */
static PyObject *
pysl_build_dims_tuple(int *dims, unsigned int num_dims)
{
    PyObject *dims_tuple;
    unsigned int i;

    if (NULL == dims) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create index tuple from NULL pointer");
	return NULL;
    }

    if (NULL == (dims_tuple = PyTuple_New(num_dims)))
        return NULL;

    for (i = 0; i < num_dims; i++)
        if (PyTuple_SetItem(dims_tuple, i, PyInt_FromLong(dims[i]))) {
            Py_DECREF(dims_tuple);
	    return NULL;
	}

    return dims_tuple;
}

/*
 * Create a S-Lang array from an ObjectArray.
 *
 * If all elements of the ObjectArray are of the same type, return an
 * appropriately-typed S-Lang array.  Otherwise, return an Any_Type array
 * (which probably won't be very useful, but it's better than refusing to
 * create the array altogether).
 */
static SLang_Array_Type *
pysl_slarray_from_objectarray(PyObject *array)
{
    SLang_Array_Type *sl_array = NULL;
    int sl_type;
    PyObject *element = NULL;
    void *data = NULL;

    PyObject *dims_tuple = NULL;
    PyObject *tmp_tuple = NULL;
    int dims[SLARRAY_MAX_DIMS];
    int *max_dims;
    unsigned int num_dims;
    unsigned int i;

    if (NULL == array) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create S-Lang array from NULL pointer");
	goto error;
    }

    if (NULL == (dims_tuple = PyObject_GetAttrString(array, "shape")))
        goto error;

    num_dims = PyTuple_Size(dims_tuple);

    /* Guess data type from first item in array */
    memset(dims, 0, sizeof(dims));
    if (NULL == (tmp_tuple = pysl_build_dims_tuple(dims, num_dims)) ||
        NULL == (element = PyObject_GetItem(array, tmp_tuple)) ||
	-1 == pysl_push_pyobject(element) ||
	-1 == (sl_type = SLang_peek_at_stack()) ||
	-1 == SLdo_pop())
    {
        goto error;
    }
    Py_DECREF(tmp_tuple);
    Py_DECREF(element);
    tmp_tuple = NULL;
    element = NULL;

    /* Store the dimensions of the ObjectArray in dims */
    for (i = 0; i < num_dims && i < SLARRAY_MAX_DIMS; i++) {
        if (NULL == (element = PyTuple_GetItem(dims_tuple, i)))
	    goto error;
	/* FIXME: do some overflow checking */
	dims[i] = PyInt_AsLong(element);
    }

    Py_DECREF(dims_tuple);
    dims_tuple = NULL;

    sl_array = SLang_create_array(sl_type, 0, NULL, dims, num_dims);
    if (NULL == sl_array)
        goto error;

    /* Allocate temporary storage for a single element of the S-Lang array */
    if (NULL == (data = malloc(sl_array->sizeof_type))) {
        PyErr_NoMemory();
	SLang_free_array(sl_array);
	sl_array = NULL;
	goto error;
    }

    max_dims = sl_array->dims;
    memset(dims, 0, sizeof(dims));

    /*
     * Loop over all the elements of the ObjectArray, transferring each one
     * to the new S-Lang array
     */
    do {
        if (NULL == (dims_tuple = pysl_build_dims_tuple(dims, num_dims)) ||
            NULL == (element = PyObject_GetItem(array, dims_tuple)) ||
	    -1 == pysl_push_pyobject(element))
	{
            SLang_free_array(sl_array);
	    sl_array = NULL;
            goto error;
	}

        Py_DECREF(dims_tuple);
        Py_DECREF(element);
	dims_tuple = NULL;
	element = NULL;

	/*
	 * If the data type of the current element differs from that of the
	 * preceding elements, convert the S-Lang array to an Any_Type array
	 */
	if (sl_type != SLANG_ANY_TYPE && sl_type != SLang_peek_at_stack()) {
	    if (-1 == SLang_push_array(sl_array, 1) ||
                -1 == SLang_pop_array_of_type(&sl_array, SLANG_ANY_TYPE))
	    {
	        goto error;
	    }

	    sl_type = SLANG_ANY_TYPE;
            max_dims = sl_array->dims;
	    free(data);

            if (NULL == (data = malloc(sl_array->sizeof_type))) {
                PyErr_NoMemory();
	        SLang_free_array(sl_array);
	        sl_array = NULL;
	        goto error;
            }
	}

        if (-1 == SLang_pop_value(sl_type, data) ||
	    -1 == SLang_set_array_element(sl_array, dims, data))
	{
            SLang_free_array(sl_array);
	    sl_array = NULL;
            goto error;
	}
    } while (0 == _SLarray_next_index(dims, max_dims, num_dims));

error:
    Py_XDECREF(element);
    free(data);
    Py_XDECREF(dims_tuple);
    Py_XDECREF(tmp_tuple);

    return sl_array;
}

/*
 * Create a S-Lang Assoc_Type from a dictionary.
 */
static int
pysl_push_dict(PyObject *dict)
{
    PyObject *key = NULL;
    PyObject *value = NULL;
    int pos = 0;
    int rv = -1;  /* Return value */

    if (NULL == dict) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create S-Lang associative array from NULL pointer");
	goto error;
    }

    /* Push new Assoc_Type onto stack */
    if (-1 == SLang_load_string("Assoc_Type[];"))
        goto error;

    /* Transfer each key/value pair to the Assoc_Type */
    while (PyDict_Next(dict, &pos, &key, &value)) {
        if (-1 == SLdup_n(1) ||
	    -1 == pysl_push_pyobject(key) ||
	    -1 == pysl_push_pyobject(value) ||
	    -1 == SLang_load_string("($0,$1,$2) = (); $0[$1] = $2;"))
	{
	    goto error;
	}
    }

    /* Indicate success */
    rv = 0;

error:

    return rv;
}

/*
 * Create a S-Lang structure from a SLangStruct object.
 */
static int
pysl_push_struct(PyObject *obj)
{
    PyObject *item_list = NULL;
    PyObject *type_str_obj = NULL;
    char *type_str = NULL;
    PyObject *item_tuple = NULL;
    PyObject *field = NULL;
    PyObject *value = NULL;
    int num_items;
    int i;
    int rv = -1;  /* Return value */

    if (NULL == obj) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create S-Lang struct from NULL pointer");
	goto error;
    }

    /* Create a S-Lang structure that corresponds to the SLangStruct */
    if (NULL == (item_list = PyObject_CallMethod(obj, "tolist", NULL)) ||
        NULL == (type_str_obj = PyObject_CallMethod(obj, "toslstring", NULL)) ||
        NULL == (type_str = PyString_AsString(type_str_obj)) ||
	-1 == SLang_load_string(type_str))
    {
        goto error;
    }

    num_items = PyList_Size(item_list);

    /* Transfer the data for each field to the new structure */
    for (i = 0; i < num_items; i++) {
        if (-1 == SLdup_n(1) ||
	    NULL == (item_tuple = PyList_GetItem(item_list, i)) ||
	    NULL == (field = PyTuple_GetItem(item_tuple, 0)) ||
	    NULL == (value = PyTuple_GetItem(item_tuple, 1)) ||
	    -1 == pysl_push_pyobject(field) ||
	    -1 == pysl_push_pyobject(value) ||
	    -1 == SLexecute_function(pysl_set_struct_field))
	{
	    goto error;
	}
    }

    /* Indicate success */
    rv = 0;

error:
    Py_XDECREF(item_list);
    Py_XDECREF(type_str_obj);

    return rv;
}

/*
 * Convert a Python object to an appropriate S-Lang object and push it onto
 * the S-Lang stack.
 */
static int
pysl_push_pyobject(PyObject *obj)
{
    long lval;
    PyObject *str = NULL;
    PyObject *py_array = NULL;
    SLang_Array_Type *sl_array = NULL;
    int status = 0;

    if (NULL == obj) {

        PyErr_SetString(PyExc_SystemError,
	  "attempted to push NULL pointer on to S-Lang stack");
	status = -1;

    } else if (obj == Py_None) {

        status = SLang_push_null();

    } else if (PyInt_Check(obj)) {

        status = SLang_push_long(PyInt_AsLong(obj));

    } else if (PyLong_Check(obj)) {

        /* This will raise OverflowError if value is greater than LONG_MAX */
        lval = PyLong_AsLong(obj);

	if (NULL != PyErr_Occurred() &&
	    PyErr_ExceptionMatches(PyExc_OverflowError))
	{
	    status = -1;
	} else {
            status = SLang_push_long(lval);
	}

    } else if (PyFloat_Check(obj)) {

        status = SLang_push_double(PyFloat_AsDouble(obj));

    } else if (PyComplex_Check(obj)) {

        status = SLang_push_complex(PyComplex_RealAsDouble(obj),
	  PyComplex_ImagAsDouble(obj));

    } else if (PyString_Check(obj)) {

        status = SLang_push_string(PyString_AsString(obj));

    } else if (PyUnicode_Check(obj)) {

        /* Convert Unicode to Python string with default encoding (ASCII) */
        if (NULL == (str = PyUnicode_AsEncodedString(obj, NULL, NULL)))
	    status = -1;
	else
            status = SLang_push_string(PyString_AsString(str));

	Py_XDECREF(str);

    } else if (PyObject_TypeCheck(obj, &PySLSLangObject_Type)) {

        status = SLang_push_anytype(((PySLSLangObject_Object*)obj)->obj);

    } else if (PyObject_TypeCheck(obj, (PyTypeObject*)PySLSLangStruct_Type)) {

        status = pysl_push_struct(obj);

    } else if (PyDict_Check(obj)) {

        status = pysl_push_dict(obj);

    } else if (PySequence_Check(obj)) {

	if (PyObject_TypeCheck(obj, (PyTypeObject*)NAObjectArray_Type)) {

	    /* Sequence is an ObjectArray */
            sl_array = pysl_slarray_from_objectarray(obj);

	} else {

	    /* We have a NumArray or a generic sequence.  First, try to make a
	     * well-behaved NumArray from it.  (If the object is already a
	     * well-behaved NumArray, this will simply return a new
	     * reference to it.) */
	    py_array = (PyObject*)NA_InputArray(obj, tAny, NUM_C_ARRAY);

	    if (NULL != py_array) {
	        /* Success */
                sl_array = pysl_slarray_from_numarray((PyArrayObject*)py_array);
	    } else {
	        /* Couldn't make a NumArray, presumably because the sequence
		 * contains non-numeric objects.  Try an ObjectArray
		 * instead. */

	        /* FIXME: We're recklessly assuming that only a type error
		 * could have occurred.  We should really check for something
		 * more specific. */
	        PyErr_Clear();

                py_array = PyObject_CallFunction(NAObjectArray_Type, "O", obj);
	        if (NULL != py_array)
                   sl_array = pysl_slarray_from_objectarray(py_array);
	    }

	    Py_XDECREF(py_array);

	}

	if (NULL == sl_array)
	    status = -1;
	else
            status = SLang_push_array(sl_array, 1);

    } else {

        PyErr_Format(PyExc_TypeError, "cannot convert %s to S-Lang object",
	  obj->ob_type->tp_name);
        status = -1;

    }

    return status;
}


/*******************************************************************************
 *
 * S-Lang to Python data conversion routines
 *
 ******************************************************************************/


/* This gets called recursively by the various pop functions */
static PyObject * pysl_pop_pyobject(void);

/*
 * Create an ObjectArray from a S-Lang array.
 */
static PyObject *
pysl_objectarray_from_slarray(SLang_Array_Type *sl_array)
{
    PyObject *array = NULL;
    PyObject *element = NULL;
    void *data = NULL;

    PyObject *dims_tuple = NULL;
    int dims[SLARRAY_MAX_DIMS];
    int *max_dims;
    unsigned int num_dims;

    if (NULL == sl_array) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create ObjectArray from NULL pointer");
	goto error;
    }

    max_dims = sl_array->dims;
    num_dims = sl_array->num_dims;

    if (NULL == (dims_tuple = pysl_build_dims_tuple(max_dims, num_dims)))
        goto error;
    Py_INCREF(Py_None);

    /* Create empty ObjectArray */
    array = PyObject_CallFunction(NAObjectArray_Type, "OO", Py_None,
      dims_tuple);

    Py_DECREF(Py_None);
    Py_DECREF(dims_tuple);
    dims_tuple = NULL;

    if (NULL == array)
        goto error;

    /* Allocate temporary storage for a single element of the S-Lang array */
    if (NULL == (data = malloc(sl_array->sizeof_type))) {
        PyErr_NoMemory();
        Py_DECREF(array);
	array = NULL;
	goto error;
    }

    memset(dims, 0, sizeof(dims));

    /* Transfer each element of the S-Lang array to the new ObjectArray */
    do {
        if (NULL == (dims_tuple = pysl_build_dims_tuple(dims, num_dims)) ||
	    -1 == SLang_get_array_element(sl_array, dims, data) ||
	    -1 == SLang_push_value(sl_array->data_type, data) ||
	    NULL == (element = pysl_pop_pyobject()) ||
	    -1 == PyObject_SetItem(array, dims_tuple, element))
	{
            Py_DECREF(array);
	    array = NULL;
            goto error;
	}

        Py_DECREF(dims_tuple);
        Py_DECREF(element);
	dims_tuple = NULL;
	element = NULL;
    } while (0 == _SLarray_next_index(dims, max_dims, num_dims));

error:
    Py_XDECREF(element);
    free(data);
    Py_XDECREF(dims_tuple);

    return array;
}

/*
 * Create a NumArray or ObjectArray from a S-Lang array.
 */
static PyObject *
pysl_pop_array(void)
{
    PyObject *array = NULL;
    SLang_Array_Type *sl_array = NULL;
    SLtype sl_type;
    NumarrayType type;
    int ndim;
    maybelong shape[SLARRAY_MAX_DIMS];
    int i;

    if (SLANG_ARRAY_TYPE != SLang_peek_at_stack()) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create array without Array_Type on S-Lang stack");
	return NULL;
    }

    if (-1 == SLang_pop_array(&sl_array, 0))
        return NULL;

    sl_type = sl_array->data_type;

    switch (sl_type) {
        case SLANG_CHAR_TYPE:
	    type = tInt8;
	    break;
        case SLANG_UCHAR_TYPE:
	    type = tUInt8;
	    break;
        case SLANG_SHORT_TYPE:
	    type = tInt16;
	    break;
        case SLANG_USHORT_TYPE:
	    type = tUInt16;
	    break;
        case SLANG_INT_TYPE:
	    type = tInt32;
	    break;
        case SLANG_UINT_TYPE:
	    type = tUInt32;
	    break;
        case SLANG_LONG_TYPE:
	    type = tLong;
	    break;
        case SLANG_ULONG_TYPE:
#if LP64
	    type = tUInt64;
#else
	    type = tUInt32;
#endif
	    break;
        case SLANG_FLOAT_TYPE:
	    type = tFloat32;
	    break;
        case SLANG_DOUBLE_TYPE:
	    type = tFloat64;
	    break;
        case SLANG_COMPLEX_TYPE:
	    type = tComplex64;
	    break;
	default: /* Handle any other array type as an ObjectArray */
            array = pysl_objectarray_from_slarray(sl_array);
            SLang_free_array(sl_array);
	    return array;
    }

    /* Cast number of dimensions from unsigned int to int and each dimension
     * from int to maybelong */
    ndim = (int)sl_array->num_dims;
    for (i = 0; i < ndim; i++)
        shape[i] = (maybelong)(sl_array->dims[i]);

    /* Create the NumArray (NA_vNewArray() allocates space for and copies the
     * data) */
    array = (PyObject*)NA_vNewArray(sl_array->data, type, ndim, shape);

    SLang_free_array(sl_array);

    return array;
}

/*
 * Create a dictionary from a S-Lang Assoc_Type.
 */
static PyObject *
pysl_pop_dict(void)
{
    PyObject *dict = NULL;
    SLang_Array_Type *key_array = NULL;
    SLang_Array_Type *value_array = NULL;
    char *key = NULL;
    SLang_Any_Type *sl_value = NULL;
    PyObject *py_value = NULL;
    unsigned int i;

    if (SLANG_ASSOC_TYPE != SLang_peek_at_stack()) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to create dict without Assoc_Type on S-Lang stack");
	goto error;
    }

    /* Get arrays of keys and values from the Assoc_Type and create the new
     * dictionary */
    if (-1 == SLdup_n(1) ||  /* Need two copies of the Assoc_Type object */
        -1 == SLexecute_function(pysl_assoc_get_keys) ||
        -1 == SLang_pop_array_of_type(&key_array, SLANG_STRING_TYPE) ||
        -1 == SLexecute_function(pysl_assoc_get_values) ||
        -1 == SLang_pop_array_of_type(&value_array, SLANG_ANY_TYPE) ||
	NULL == (dict = PyDict_New()))
    {
	goto error;
    }

    /* Transfer the key/value pairs to the new dictionary */
    for (i = 0; i < key_array->dims[0]; i++) {
        if (-1 == SLang_get_array_element(key_array, &i, &key) ||
	    -1 == SLang_get_array_element(value_array, &i, &sl_value) ||
	    -1 == SLang_push_anytype(sl_value) ||
	    NULL == (py_value = pysl_pop_pyobject()) ||
	    -1 == PyDict_SetItemString(dict, key, py_value))
	{
	    Py_DECREF(dict);
	    dict = NULL;
	    goto error;
	}

	/* Free temporary storage for the current dict item and set the
	 * pointers to NULL in case we do an error exit on the next pass */
        SLang_free_slstring(key);
        SLang_free_anytype(sl_value);
	Py_DECREF(py_value);
	key = NULL;
	sl_value = NULL;
	py_value = NULL;
    }

error:
    SLang_free_array(key_array);
    SLang_free_array(value_array);
    SLang_free_slstring(key);
    SLang_free_anytype(sl_value);
    Py_XDECREF(py_value);

    return dict;
}

/*
 * Create a SLangStruct object from a S-Lang structure.
 */
static PyObject *
pysl_pop_struct(void)
{
    PyObject *obj = NULL;
    PyObject *item_list = NULL;
    SLang_Array_Type *field_array = NULL;
    char *field = NULL;
    PyObject *value = NULL;
    PyObject *item_tuple = NULL;
    int sl_type;
    char *type_name = NULL;
    PyObject *type_name_obj = NULL;
    unsigned int i;

    /* Get array of field names and create new list for holding field/value
     * tuples */
    if (-1 == SLdup_n(1) ||
        -1 == SLexecute_function(pysl_get_struct_field_names) ||
        -1 == SLang_pop_array_of_type(&field_array, SLANG_STRING_TYPE) ||
	NULL == (item_list = PyList_New(field_array->dims[0])))
    {
	goto error;
    }

    /* Transfer each field/value pair to a tuple and add to the list */
    for (i = 0; i < field_array->dims[0]; i++) {
        if (-1 == SLdup_n(1) ||
	    -1 == SLang_get_array_element(field_array, &i, &field) ||
	    -1 == SLang_push_string(field) ||
            -1 == SLexecute_function(pysl_get_struct_field) ||
	    NULL == (value = pysl_pop_pyobject()) ||
	    NULL == (item_tuple = Py_BuildValue("(sO)", field, value)) ||
	    (Py_INCREF(item_tuple), 0) || /* Make sure we keep a reference */
	    -1 == PyList_SetItem(item_list, (int)i, item_tuple))
	{
	    goto error;
	}

        SLang_free_slstring(field);
	Py_DECREF(value);
	Py_DECREF(item_tuple);
	field = NULL;
	value = NULL;
	item_tuple = NULL;
    }

    /* Get the type name of the S-Lang struct (either "Struct_Type" or a
     * user-defined type) */
    if (-1 == (sl_type = SLang_peek_at_stack()) ||
        -1 == SLdo_pop() ||  /* Remove S-Lang structure from stack */
	NULL == (type_name = SLclass_get_datatype_name(sl_type)) ||
        NULL == (type_name_obj = PyString_FromString(type_name)))
    {
        goto error;
    }

    /* Create the SLangStruct object */
    obj = PyObject_CallFunction(PySLSLangStruct_Type, "OO", item_list,
      type_name_obj);

error:
    Py_XDECREF(item_list);
    SLang_free_array(field_array);
    SLang_free_slstring(field);
    Py_XDECREF(value);
    Py_XDECREF(item_tuple);
    Py_XDECREF(type_name_obj);

    return obj;
}

/*
 * Pop a S-Lang object off the S-Lang stack and convert it to an appropriate
 * Python object.
 */
static PyObject *
pysl_pop_pyobject(void)
{
    PyObject *obj = NULL;
    int sl_type;
    long lval;
    unsigned long ulval;
    double dval;
    double reval, imval;
    char *sval = NULL;
    int is_struct = 0;
    SLang_Any_Type *val = NULL;

    if (-1 == (sl_type = SLang_peek_at_stack())) {
        PyErr_SetString(PyExc_SystemError,
	  "attempted to pop object off empty S-Lang stack");
	return NULL;
    }

    switch (sl_type) {
        case SLANG_NULL_TYPE:
	    if (-1 != SLang_pop_null()) {
                Py_INCREF(Py_None);
                obj = Py_None;
	    }
	    break;

        case SLANG_CHAR_TYPE:
        case SLANG_UCHAR_TYPE:
        case SLANG_SHORT_TYPE:
        case SLANG_USHORT_TYPE:
        case SLANG_INT_TYPE:
#if LP64
        case SLANG_UINT_TYPE:
#endif
        case SLANG_LONG_TYPE:
	    if (-1 != SLang_pop_long(&lval))
	        obj = PyInt_FromLong(lval);
	    break;

	/* These might overflow a long, so pop them as unsigned longs and
	 * create Python longs from them */
#if !LP64
        case SLANG_UINT_TYPE:
#endif
	case SLANG_ULONG_TYPE:
	    if (-1 != SLang_pop_ulong(&ulval))
	        obj = PyLong_FromUnsignedLong(ulval);
	    break;

        case SLANG_FLOAT_TYPE:
        case SLANG_DOUBLE_TYPE:
	    if (-1 != SLang_pop_double(&dval, NULL, NULL))
	        obj = PyFloat_FromDouble(dval);
	    break;

        case SLANG_COMPLEX_TYPE:
	    if (-1 != SLang_pop_complex(&reval, &imval))
	        obj = PyComplex_FromDoubles(reval, imval);
	    break;

        case SLANG_STRING_TYPE:
	    if (-1 != SLang_pop_slstring(&sval)) {
	        obj = PyString_FromString(sval);
		SLang_free_slstring(sval);
	    }
	    break;
	    
        case SLANG_ARRAY_TYPE:
	    obj = pysl_pop_array();
	    break;

        case SLANG_ASSOC_TYPE:
	    obj = pysl_pop_dict();
	    break;

        case SLANG_STRUCT_TYPE:
	    obj = pysl_pop_struct();
	    break;

	default:
	    /* Test whether object is a user-defined struct type */
            if (-1 == SLdup_n(1) ||
                -1 == SLexecute_function(pysl_is_struct_type) ||
		-1 == SLang_pop_integer(&is_struct))
	    {
	        return NULL;
	    }

	    if (is_struct) {

	        obj = pysl_pop_struct();

	    } else if (-1 != SLang_pop_anytype(&val)) {

	        /* Handle anything else as an opaque SLangObject */
                obj = (PyObject*)PyObject_New(PySLSLangObject_Object,
	          &PySLSLangObject_Type);
	        ((PySLSLangObject_Object*)obj)->obj = val;

	    }
	    break;
    }

    return obj;
}


/*******************************************************************************
 *
 * Public module functions
 *
 ******************************************************************************/


PyDoc_STRVAR(pysl_load_string__doc__,
"Evaluate a string of S-Lang code, optionally in a specified namespace:\n\
\n\
>>> load_string('define ls_f(x) { return x*x; }')\n\
>>> sl.ls_f(7)\n\
49\n\
>>> load_string('define ls_f(x) { return x + \"bar\"; }', 'ls_ns')\n\
>>> sl.ls_ns.ls_f('foo')\n\
'foobar'\n\
");

static PyObject *
pysl_load_string(PyObject *self, PyObject *args)
{
    char *str;
    char *ns_name = NULL;

    if (!PyArg_ParseTuple(args, "s|z", &str, &ns_name))
        return NULL;

    if (-1 == SLns_load_string(str, ns_name)) {
        pysl_slang_restart();
	return NULL;
    }

    Py_INCREF(Py_None);
    return Py_None;
}


PyDoc_STRVAR(pysl_load_file__doc__,
"Load a file containing S-Lang code, optionally in a specified namespace:\n\
\n\
>>> from tempfile import mktemp ; slfile = file(mktemp(), 'w')\n\
>>> print >> slfile, 'variable lf_a = [1, 2, 3];'\n\
>>> slfile.close()\n\
>>> load_file(slfile.name)\n\
>>> sl.lf_a\n\
array([1, 2, 3])\n\
>>> slfile = file(slfile.name, 'w')\n\
>>> print >> slfile, 'variable lf_b = 71.9;'\n\
>>> slfile.close()\n\
>>> load_file(slfile.name, 'lf_ns')\n\
>>> print sl.lf_ns.lf_b\n\
71.9\n\
>>> from os import remove; remove(slfile.name)\n\
");

static PyObject *
pysl_load_file(PyObject *self, PyObject *args)
{
    char *file_name;
    char *ns_name = NULL;

    if (!PyArg_ParseTuple(args, "s|z", &file_name, &ns_name))
        return NULL;

    if (-1 == SLns_load_file(file_name, ns_name)) {
        pysl_slang_restart();
	return NULL;
    }

    Py_INCREF(Py_None);
    return Py_None;
}


PyDoc_STRVAR(pysl_is_defined__doc__,
"Test whether a name is known to the S-Lang interpreter as a variable or\n\
function, optionally in a specified namespace.  The function returns one of\n\
the following values:\n\
\n\
  +1 for an intrinsic function\n\
  +2 for a user defined function\n\
  -1 for an intrinsic variable\n\
  -2 for a user defined variable\n\
   0 if the name is undefined\n\
\n\
>>> is_defined('message')\n\
1\n\
>>> is_defined('yam', 'id_ns')\n\
0\n\
>>> load_string('variable id_yam = 6;', 'id_ns')\n\
>>> is_defined('id_yam', 'id_ns')\n\
-2\n\
");

static PyObject *
pysl_is_defined(PyObject *self, PyObject *args)
{
    PyObject *rtn_val = NULL;
    char *name = NULL;
    char *ns_name = NULL;
    char *ns_qual_name = NULL;

    if (!PyArg_ParseTuple(args, "s|z", &name, &ns_name))
        goto error;

    if (NULL == (ns_qual_name = pysl_ns_qualify_name(name, ns_name)))
        goto error;

    rtn_val = Py_BuildValue("i", SLang_is_defined(ns_qual_name));

error:
    free(ns_qual_name);

    return rtn_val;
}


PyDoc_STRVAR(pysl_is_namespace__doc__,
"Return true if the given name is a S-Lang namespace, false otherwise:\n\
\n\
>>> is_namespace('foobarblah')\n\
0\n\
>>> load_string('variable in_ff = 2.4;', 'in_ns')\n\
>>> is_namespace('in_ns')\n\
1\n\
");

static PyObject *
pysl_is_namespace(PyObject *self, PyObject *args)
{
    char *name;

    if (!PyArg_ParseTuple(args, "s", &name))
        return NULL;

    return Py_BuildValue("i", (int)(NULL != _SLns_find_namespace(name)));
}


PyDoc_STRVAR(pysl_get_variable__doc__,
"Return the value of a S-Lang variable, optionally from a specified\n\
namespace:\n\
\n\
>>> load_string('variable gv_a = \"Hello, world!\";')\n\
>>> get_variable('gv_a')\n\
'Hello, world!'\n\
>>> load_string('variable gv_ns_a = \"Hello, namespace!\";', 'gv_ns')\n\
>>> get_variable('gv_ns_a', 'gv_ns')\n\
'Hello, namespace!'\n\
>>> get_variable('gv_a', 'gv_ns')\n\
Traceback (most recent call last):\n\
...\n\
NameError: S-Lang variable 'gv_ns->gv_a' is not defined\n\
");

static PyObject *
pysl_get_variable(PyObject *self, PyObject *args)
{
    PyObject *value = NULL;
    char *name = NULL;
    char *ns_name = NULL;
    char *ns_qual_name = NULL;

    if (!PyArg_ParseTuple(args, "s|z", &name, &ns_name))
        goto error;

    if (NULL == (ns_qual_name = pysl_ns_qualify_name(name, ns_name)))
        goto error;

    if (SLang_is_defined(ns_qual_name) >= 0) {
        PyErr_Format(PySLNameError,
	  "S-Lang variable '%s' is not defined", ns_qual_name);
        goto error;
    }

    if (-1 == SLns_load_string(name, ns_name) ||
        NULL == (value = pysl_pop_pyobject()))
    {
        pysl_slang_restart();
        goto error;
    }

error:
    free(ns_qual_name);

    return value;
}


PyDoc_STRVAR(pysl_set_variable__doc__,
"Set the value of S-Lang variable, optionally in a specified namespace.  If\n\
the variable does not yet exist, it is declared automatically:\n\
\n\
>>> sl.sv_var\n\
Traceback (most recent call last):\n\
...\n\
NameError: S-Lang variable 'sv_var' is not defined\n\
>>> set_variable('sv_var', 123)\n\
>>> sl.sv_var\n\
123\n\
>>> set_variable('sv_var', 'spam')\n\
>>> sl.sv_var\n\
'spam'\n\
>>> set_variable('sv_ns_other_var', 'ham', 'sv_ns')\n\
>>> sl.sv_ns.sv_ns_var\n\
Traceback (most recent call last):\n\
...\n\
NameError: S-Lang variable 'sv_ns->sv_ns_var' is not defined\n\
>>> set_variable('sv_ns_var', 'ham', 'sv_ns')\n\
>>> sl.sv_ns.sv_ns_var\n\
'ham'\n\
");

static PyObject *
pysl_set_variable(PyObject *self, PyObject *args)
{
    PyObject *value = NULL;
    PyObject *rtn_value = NULL;
    char *name = NULL;
    char *ns_name = NULL;
    char *ns_qual_name = NULL;
    char *tmp_str = NULL;
    int is_def;

    if (!PyArg_ParseTuple(args, "sO|z", &name, &value, &ns_name))
        goto error;

    if (NULL == (ns_qual_name = pysl_ns_qualify_name(name, ns_name)))
        goto error;

    is_def = SLang_is_defined(ns_qual_name);

    if (is_def > 0) {
        PyErr_Format(PySLNameError, "'%s' is a S-Lang function name",
	  ns_qual_name);
        goto error;
    }

    /* If variable doesn't exist, declare it */
    if (0 == is_def) {
        if (NULL == (tmp_str = pysl_strcatn(3, "variable ", name, ";")) ||
	    -1 == SLns_load_string(tmp_str, ns_name))
	{
            pysl_slang_restart();
	    goto error;
	}

	free(tmp_str);
	tmp_str = NULL;
    }

    if (-1 == pysl_push_pyobject(value) ||
        NULL == (tmp_str = pysl_strcatn(2, name, " = ();")) ||
        -1 == SLns_load_string(tmp_str, ns_name))
    {
        pysl_slang_restart();
        goto error;
    }

    Py_INCREF(Py_None);
    rtn_value = Py_None;

error:
    free(ns_qual_name);
    free(tmp_str);

    return rtn_value;
}


PyDoc_STRVAR(pysl_get_function__doc__,
"Return a SLangName pointer to the named S-Lang function, optionally from a\n\
specified namespace.  The function can then be executed by passing the\n\
SLangName to execute_function():\n\
\n\
>>> gf_f = get_function('gf_f')\n\
Traceback (most recent call last):\n\
...\n\
NameError: S-Lang function 'gf_f' is not defined\n\
>>> load_string('define gf_f() { return 1; }')\n\
>>> gf_f = get_function('gf_f')\n\
>>> print gf_f\n\
gf_f\n\
>>> load_string('define gf_ns_f() { return 1; }', 'gf_ns')\n\
>>> gf_ns_f = get_function('gf_ns_f', 'gf_ns')\n\
>>> print gf_ns_f\n\
gf_ns_f\n\
");

static PyObject *
pysl_get_function(PyObject *self, PyObject *args)
{
    SLang_Name_Type *nt = NULL;
    PySLSLangName_Object *nt_object = NULL;
    char *name = NULL;
    char *ns_name = NULL;
    char *ns_qual_name = NULL;

    if (!PyArg_ParseTuple(args, "s|z", &name, &ns_name))
        goto error;

    if (NULL == (ns_qual_name = pysl_ns_qualify_name(name, ns_name)))
        goto error;

    if (NULL == (nt = SLang_get_function(ns_qual_name))) {
        PyErr_Format(PySLNameError,
	  "S-Lang function '%s' is not defined", ns_qual_name);
	goto error;
    }

    nt_object = PyObject_New(PySLSLangName_Object, &PySLSLangName_Type);
    nt_object->nt = nt;

error:
    free(ns_qual_name);

    return (PyObject*)nt_object;
}


PyDoc_STRVAR(pysl_execute_function__doc__,
"Given a SLangName and a tuple of objects, execute the function pointed to\n\
by the SLangName, passing the objects as arguments, and return any return\n\
values:\n\
\n\
>>> load_string(\"\"\"\n\
... define ef_square_args() {\n\
...    variable args = __pop_args(_NARGS);\n\
...    variable x;\n\
...    foreach (args) {\n\
...       x = ();\n\
...       x.value*x.value;\n\
...    }\n\
... }\"\"\")\n\
>>> ef_sa = get_function('ef_square_args')\n\
>>> execute_function(ef_sa, ())\n\
>>> execute_function(ef_sa, (3,))\n\
9\n\
>>> execute_function(ef_sa, (3,4))\n\
(9, 16)\n\
>>> execute_function(ef_sa, (3,4,5))\n\
(9, 16, 25)\n\
");

static PyObject *
pysl_execute_function(PyObject *self, PyObject *args)
{
    PyObject *nt_object = NULL;
    SLang_Name_Type *nt = NULL;
    PyObject *func_args = NULL;
    PyObject *obj = NULL;
    PyObject *rtn_obj = NULL;
    int nargs;
    int nrtn;
    int i;

    if (!PyArg_ParseTuple(args, "O!O!", &PySLSLangName_Type, &nt_object,
        &PyTuple_Type, &func_args))
    {
        return NULL;
    }

    if (NULL == (nt = ((PySLSLangName_Object*)nt_object)->nt)) {
        PyErr_SetString(PyExc_SystemError, "NULL pointer in SLangName");
        return NULL;
    }

    nargs = PyTuple_Size(func_args);
    nrtn = _SLstack_depth();

    /* Push arguments onto stack */
    SLang_start_arg_list();
    for (i = 0; i < nargs; i++) {
        if (NULL == (obj = PyTuple_GetItem(func_args, i)) ||
	    -1 == pysl_push_pyobject(obj))
	{
	    /* Remove the arguments we've already pushed onto the stack */
            SLdo_pop_n(i);

	    pysl_slang_restart();
            return NULL;
	}
    }
    SLang_end_arg_list();

    /* Execute the function */
    if (-1 == SLexecute_function(nt)) {
        pysl_slang_restart();
        return NULL;
    }

    /*
     * Collect and return the return values
     */

    nrtn = _SLstack_depth() - nrtn;

    if (nrtn < 1) {

       /* Case 1: no return value */

       Py_INCREF(Py_None);
       rtn_obj = Py_None;

    } else if (nrtn == 1) {

       /* Case 2: one return value */

       if (NULL == (rtn_obj = pysl_pop_pyobject())) {
	   SLdo_pop();
	   pysl_slang_restart();
	   return NULL;
       }

    } else {

       /* Case 3: more than one return value */

       if (NULL == (rtn_obj = PyTuple_New(nrtn))) {
	   SLdo_pop_n(nrtn);
	   pysl_slang_restart();
	   return NULL;
       }

       for (i = nrtn - 1; i >= 0; ) { /* i decremented in if test */
	   if (NULL == (obj = pysl_pop_pyobject()) ||
	       PyTuple_SetItem(rtn_obj, i--, obj))
	   {
	       SLdo_pop_n(i+1);
	       Py_DECREF(rtn_obj);
	       pysl_slang_restart();
	       return NULL;
	   }
       }

    }

    return rtn_obj;
}


/*******************************************************************************
 *
 * Module initialization
 *
 ******************************************************************************/


static PyMethodDef PySLMethods[] = {
    {"load_string",  pysl_load_string, METH_VARARGS,
     pysl_load_string__doc__},
    {"load_file",  pysl_load_file, METH_VARARGS,
     pysl_load_file__doc__},
    {"is_defined",  pysl_is_defined, METH_VARARGS,
     pysl_is_defined__doc__},
    {"is_namespace",  pysl_is_namespace, METH_VARARGS,
     pysl_is_namespace__doc__},
    {"get_variable",  pysl_get_variable, METH_VARARGS,
     pysl_get_variable__doc__},
    {"set_variable",  pysl_set_variable, METH_VARARGS,
     pysl_set_variable__doc__},
    {"get_function",  pysl_get_function, METH_VARARGS,
     pysl_get_function__doc__},
    {"execute_function",  pysl_execute_function, METH_VARARGS,
     pysl_execute_function__doc__},
    {NULL, NULL, 0, NULL}	/* Sentinel */
};

PyMODINIT_FUNC
init_pysl(void)
{
    PyObject *m;	/* Module object */
    static int slang_initialized = 0;

    /*
     * Initialize the S-Lang interpreter
     */

    if (!slang_initialized) {
        if (-1 == SLang_init_all() ||
            -1 == SLang_init_array_extra() ||
            -1 == SLang_init_import())
        {
            PyErr_SetString(PyExc_ImportError,
	      "unable to initialize S-Lang interpreter");
            return;
        }

	slang_initialized = 1;
    }

    /* Set our S-Lang error handler */
    SLang_Error_Hook = pysl_slang_error_handler;

    /*
     * Get pointers to needed Python types
     */

    NAObjectArray_Type = PyRun_String("numarray.objects.ObjectArray",
      Py_eval_input, PyEval_GetGlobals(), PyEval_GetLocals());

    PySLSLangStruct_Type = PyRun_String("SLangStruct", Py_eval_input,
      PyEval_GetGlobals(), PyEval_GetLocals());

    if (NULL == NAObjectArray_Type || NULL == PySLSLangStruct_Type)
        return;

    /*
     * Get pointers to needed S-Lang intrinsic functions
     */

    pysl_assoc_get_keys = SLang_get_function("assoc_get_keys");
    pysl_assoc_get_values = SLang_get_function("assoc_get_values");
    pysl_is_struct_type = SLang_get_function("is_struct_type");
    pysl_get_struct_field_names = SLang_get_function("get_struct_field_names");
    pysl_get_struct_field = SLang_get_function("get_struct_field");
    pysl_set_struct_field = SLang_get_function("set_struct_field");

    if (NULL == pysl_assoc_get_keys ||
	NULL == pysl_assoc_get_values ||
	NULL == pysl_is_struct_type ||
	NULL == pysl_get_struct_field_names ||
	NULL == pysl_get_struct_field ||
	NULL == pysl_set_struct_field)
    {
        PyErr_SetString(PyExc_ImportError,
          "unable to access necessary S-Lang instrinsic functions");
        return;
    }

    /*
     * Create the module object and initialize libnumarray
     */

    m = Py_InitModule("_pysl", PySLMethods);
    import_libnumarray();

    /*
     * Create the SLangName and SLangObject types.  Note that we *don't* want
     * to call PyType_Ready() because doing so will allow these types to be
     * instantiated by the user.  (We're just mimicking what's done for
     * PyCObject.)
     */

    PySLSLangName_Type.ob_type = &PyType_Type;
    Py_INCREF(&PySLSLangName_Type);
    PyModule_AddObject(m, "SLangName", (PyObject*)&PySLSLangName_Type);

    PySLSLangObject_Type.ob_type = &PyType_Type;
    Py_INCREF(&PySLSLangObject_Type);
    PyModule_AddObject(m, "SLangObject", (PyObject*)&PySLSLangObject_Type);

    /*
     * Create the SLangError and NameError exceptions
     */

    PySLSLangError = PyErr_NewException("pysl.SLangError", NULL, NULL);
    Py_INCREF(PySLSLangError);
    PyModule_AddObject(m, "SLangError", PySLSLangError);

    PySLNameError = PyErr_NewException("pysl.NameError", NULL, NULL);
    Py_INCREF(PySLNameError);
    PyModule_AddObject(m, "NameError", PySLNameError);
}

